﻿using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Scripting;
using Microsoft.Scripting.Ast;
using Microsoft.Linq.Expressions;

namespace DlrBasic.Parser.Ast
{
    class ArithmeticOperators : BinaryExpressionList
    {
        static Dictionary<int, OperatorInfo> KeywordMap;

        static ArithmeticOperators()
        {
            KeywordMap = new Dictionary<int, OperatorInfo>();
            // KeywordMap.Add(Operators.PowerId, new OperatorInfo("Power", 70));
            KeywordMap.Add(Operators.MultiplyId, new OperatorInfo("Multiply", 50));
            KeywordMap.Add(Operators.DivideId, new OperatorInfo("Division", 50));
            KeywordMap.Add(Operators.IntDivideId, new OperatorInfo("IntDivision", 50));
            KeywordMap.Add(Keywords.ModKeywordId, new OperatorInfo("ReminderInt", 50));
            KeywordMap.Add(Operators.AddId, new OperatorInfo("Add", 30));
            KeywordMap.Add(Operators.SubstractId, new OperatorInfo("Subtract", 30));
        }

        public ArithmeticOperators(SourceSpan span, DlrBasicExpression[] expressions, int[] operatorIds)
            : base(span, expressions, operatorIds)
        {
        }

        protected internal override Expression Generate(DlrBasicGenerator tg)
        {
            List<Expression> result = new List<Expression>();
            foreach (DlrBasicExpression expr in Expressions)
            {
                result.Add(expr.Generate(tg));
            }
            List<int> operators = new List<int>(this.OperatorIds);

            while(operators.Count > 0)
            {                
                int i = 0;
                while (i < operators.Count)
                {
                    OperatorInfo operatorInfo = KeywordMap[operators[i]];
                    if (i + 1 < operators.Count)
                    {
                        OperatorInfo nextOperatorInfo = KeywordMap[operators[i + 1]];
                        if (operatorInfo.Priority < nextOperatorInfo.Priority)
                        {
                            // skip if next operator priority is higher
                            i++;
                            continue; 
                        }
                    }

                    System.Reflection.MethodInfo method;
                    Expression converted1, converted2;

                    TypeConversionUtils.FindMethodAndConvertOperands(typeof(Runtime.DlrBasicMath),
                        operatorInfo.MethodPrefix, result[i], result[i + 1],
                        out method, out converted1, out converted2);


                    Expression expr = Expression.Call(method, converted1, converted2);

                    operators.RemoveAt(i);
                    result.RemoveAt(i); result.RemoveAt(i);
                    result.Insert(i, expr);
                }
            }
            return result[0];
        }

        struct OperatorInfo
        {
            internal string MethodPrefix;
            internal int Priority;

            internal OperatorInfo(string methodPrefix, int priority)
            {
                this.MethodPrefix = methodPrefix;
                this.Priority = priority;
            }
        }
    }

    class ArithmeticPowerOperators : BinaryExpressionList
    {
        const string PowerMethodPrefix = "Power";

        public ArithmeticPowerOperators(SourceSpan span, DlrBasicExpression[] expressions, int[] operatorIds)
            : base(span, expressions, operatorIds)
        {
        }

        protected internal override Expression Generate(DlrBasicGenerator tg)
        {
            Expression result = Expressions[0].Generate(tg);
            for (int i = 1; i < Expressions.Length; i++)
            {
                System.Reflection.MethodInfo method;
                Expression converted1, converted2;

                TypeConversionUtils.FindMethodAndConvertOperands(typeof(Runtime.DlrBasicMath),
                    PowerMethodPrefix, result, Expressions[i].Generate(tg),
                    out method, out converted1, out converted2);

                result = Expression.Call(method, converted1, converted2);                
            }
            return result;
        }
    }
}
